use std::ops::{Add, Div, Mul, Neg, Sub};

use crate::{Transform, Transformation};

#[derive(Clone, Copy, Debug)]
pub struct Vector {
	x: f32,
	y: f32,
}
impl Vector {
	pub fn new(x: f32, y: f32) -> Self {
		Self { x, y }
	}
	pub fn x(self) -> f32 {
		self.x
	}
	pub fn y(self) -> f32 {
		self.y
	}
	pub fn length(self) -> f32 {
		self.x.hypot(self.y)
	}
	pub fn dot(self, other: Self) -> f32 {
		self.x * other.x + self.y * other.y
	}
	pub fn cross(self, other: Self) -> f32 {
		self.x * other.y - other.x * self.y
	}
	pub fn normalize(self) -> Option<Self> {
		let length = self.length();
		if length == 0.0 {
			None
		} else {
			Some(self / length)
		}
	}
	pub fn perpendicular(self) -> Self {
		Self { x: -self.y, y: self.x }
	}
}
impl Add for Vector {
	type Output = Self;

	fn add(self, other: Self) -> Self::Output {
		Self {
			x: self.x + other.x,
			y: self.y + other.y,
		}
	}
}
impl Sub for Vector {
	type Output = Self;

	fn sub(self, other: Self) -> Self::Output {
		Self {
			x: self.x - other.x,
			y: self.y - other.y,
		}
	}
}
impl Mul<f32> for Vector {
	type Output = Self;

	fn mul(self, k: f32) -> Self::Output {
		Self {
			x: self.x * k,
			y: self.y * k,
		}
	}
}
impl Div<f32> for Vector {
	type Output = Self;

	fn div(self, k: f32) -> Self::Output {
		Self {
			x: self.x / k,
			y: self.y / k,
		}
	}
}
impl Neg for Vector {
	type Output = Self;

	fn neg(self) -> Self::Output {
		Self { x: -self.x, y: -self.y }
	}
}
impl Transform for Vector {
	fn transform(self, t: &impl Transformation) -> Self	{
		t.transform_vector(self)
	}
	fn transform_mut(&mut self, t: &impl Transformation)	{
		*self = self.transform(t);
	}
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Point {
	x: f32,
	y: f32,
}
impl Point {
	pub fn new(x: f32, y: f32) -> Self {
		Self { x, y }
	}
	pub fn x(self) -> f32 {
		self.x
	}
	pub fn y(self) -> f32 {
		self.y
	}
	pub fn min(self, other: Self) -> Self {
		if self <= other {
			self
		} else {
			other
		}
	}
	pub fn max(self, other: Self) -> Self {
		if self <= other {
			other
		} else {
			self
		}
	}
	pub fn lerp(self, other: Self, t: f32) -> Self {
		Self {
			x: self.x * (1.0 - t) + other.x * t,
			y: self.y * (1.0 - t) + other.y * t,
		}
	}
}
impl Add<Vector> for Point {
	type Output = Self;
	fn add(self, v: Vector) -> Self::Output {
		Self {
			x: self.x + v.x(),
			y: self.y + v.y(),
		}
	}
}
impl Sub for Point {
	type Output = Vector;
	fn sub(self, other: Self) -> Self::Output {
		Vector::new(self.x() - other.x(), self.y() - other.y())
	}
}
impl Sub<Vector> for Point {
	type Output = Self;
	fn sub(self, v: Vector) -> Self::Output {
		Self {
			x: self.x - v.x(),
			y: self.y - v.y(),
		}
	}
}
impl Transform for Point {
	fn transform(self, t: &impl Transformation) -> Self	{
		t.transform_point(self)
	}
	fn transform_mut(&mut self, t: &impl Transformation)	{
		*self = self.transform(t);
	}
}

#[derive(Clone, Copy, Debug)]
pub struct Rectangle {
	start: Point,
	end: Point,
}
impl Rectangle {
	pub fn new(start: Point, end: Point) -> Self {
		Self { start, end }
	}
	pub fn start(self) -> Point {
		self.start
	}
	pub fn end(self) -> Point {
		self.end
	}
	pub fn clamp(self, point: Point) -> Point {
		Point::new(
			point.x().max(self.start.x()).min(self.end.x()),
			point.y().max(self.start.y()).min(self.end.y()),
		)
	}
	pub fn intersect(self, other: Self) -> Self {
		Self {
			start: Point::new(self.start.x().max(other.start.x()), self.start.y().max(other.start.y())),
			end: Point::new(self.end.x().min(other.end.x()), self.end.y().min(other.end.y())),
		}
	}
}
impl Transform for Rectangle {
	fn transform(self, t: &impl Transformation) -> Self	{
		Self {
			start: t.transform_point(self.start),
			end: t.transform_point(self.end),
		}
	}
	fn transform_mut(&mut self, t: &impl Transformation)	{
		*self = self.transform(t);
	}
}
